package com.wangp.demo.anno.processor;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import sun.misc.Unsafe;

/**
 * @author <a href="mailto:wangpeng@i-baby.com" rel="nofollow">wp☠😘✨</a>
 * @version 0.0.1
 * @since 2025/8/16 15:28
 */
public class ProcessEnvUtil {

    public static void prepareEvn() {
        disableJava9SillyWarning();
        addOpensModule();
    }

    public static <T> T jbUnwrap(Class<? extends T> iface, T wrapper) {
        T unwrapped = null;
        try {
            final Class<?> apiWrappers = wrapper.getClass().getClassLoader().loadClass("org.jetbrains.jps.javac.APIWrappers");
            final Method unwrapMethod = apiWrappers.getDeclaredMethod("unwrap", Class.class, Object.class);
            unwrapped = iface.cast(unwrapMethod.invoke(null, iface, wrapper));
        } catch (Throwable ignored) {}
        return unwrapped != null ? unwrapped : wrapper;
    }

    private static void disableJava9SillyWarning() {
        try {
            Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field logger = cls.getDeclaredField("logger");
            Unsafe u = UnsafeHelper.getUnsafe();
            u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
        } catch (Throwable ignored) {}
    }

    private static void addOpensModule() {
        Class<?> cModule;
        try {
            cModule = Class.forName("java.lang.Module");
        } catch (ClassNotFoundException e) {
            return;
        }
        Object jdkCompilerModule = getJdkCompilerModule();
        Object ownModule = getOwnModule();
        String[] javacPackages = {
            "com.sun.tools.javac.api",
            "com.sun.tools.javac.code",
            "com.sun.tools.javac.comp",
            "com.sun.tools.javac.file",
            "com.sun.tools.javac.jvm",
            "com.sun.tools.javac.launcher",
            "com.sun.tools.javac.main",
            "com.sun.tools.javac.model",
            "com.sun.tools.javac.parser",
            "com.sun.tools.javac.platform",
            "com.sun.tools.javac.processing",
            "com.sun.tools.javac.resources",
            "com.sun.tools.javac.tree",
            "com.sun.tools.javac.util",
        };
        Unsafe unsafe = UnsafeHelper.getUnsafe();
        try {
            Method m = cModule.getDeclaredMethod("implAddOpens", String.class, cModule);
            long firstFieldOffset = getFirstFieldOffset(unsafe);
            unsafe.putBooleanVolatile(m, firstFieldOffset, true);
            for (String p : javacPackages) {
                // noinspection JavaReflectionInvocation
                m.invoke(jdkCompilerModule, p, ownModule);
            }
        } catch (Exception e) {
            throw ExceptionUtils.wrap(e);
        }
    }

    private static Object getOwnModule() {
        try {
            Method m = Permit.getMethod(Class.class, "getModule");
            return m.invoke(ValueChangeTraceProcessor.class);
        } catch (Exception e) {
            throw ExceptionUtils.wrap(e);
        }
    }

    private static Object getJdkCompilerModule() {
        try {
            Class<?> cModuleLayer = Class.forName("java.lang.ModuleLayer");
            Method mBoot = cModuleLayer.getDeclaredMethod("boot");
            Object bootLayer = mBoot.invoke(null);
            Class<?> cOptional = Class.forName("java.util.Optional");
            Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class);
            Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler");
            return cOptional.getDeclaredMethod("get").invoke(oCompilerO);
        } catch (Exception e) {
            throw ExceptionUtils.wrap(e);
        }
    }

    private static long getFirstFieldOffset(Unsafe unsafe) {
        try {
            return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first"));
        } catch (NoSuchFieldException e) {
            // can't happen.
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            // can't happen
            throw new RuntimeException(e);
        }
    }
}
